// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
/* Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * imx307 mipi-csi sensor driver code for LomboTech
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 */

#include <linux/init.h>
#include <linux/module.h>

#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/viss_isp.h>

#include <media/lombo_viss.h>

#include <mach/debug.h>
#include "../ccc.h"

#define VMAX (0x465)

/*
 * 2lane init
 */
static const cc_reg_t init_reg_list[] = {
	/* sensor stanby */
	{0x3000, 0x01},
	{0x3001, 0x01},
	{0x3002, 0x01},
	/* setting */
	{0x3005, 0x01},
	{0x3007, 0x00},
	{0x3009, 0x02},
	{0x300A, 0xF0},
	{0x300B, 0x00},
	{0x3011, 0x0A},
	{0x3018, VMAX&0xFF},/* V max */
	{0x3019, VMAX>>8},
	{0x301C, 0x30},
	{0x301D, 0x11},/* H max */
	{0x3046, 0x01},
	{0x304B, 0x0A},
	{0x305C, 0x18},
	{0x305D, 0x03},
	{0x305E, 0x20},
	{0x305F, 0x01},
	{0x309E, 0x4A},
	{0x309F, 0x4A},

	{0x311C, 0x0E},
	{0x3128, 0x04},
	{0x3129, 0x00},
	{0x313B, 0x41},
	{0x315E, 0x1A},
	{0x3164, 0x1A},
	{0x317C, 0x00},
	{0x31EC, 0x0E},

	/* These registers are set in CSI-2 interface only. */
	{0x3405, 0x10},
	{0x3407, 0x01},
	{0x3414, 0x0A},
	{0x3418, 0x49},
	{0x3419, 0x04},/* y-out size */
	{0x3441, 0x0C},/* 0x0A: RAW10, 0x0C: RAW12 */
	{0x3442, 0x0C},/* 0x0A: RAW10, 0x0C: RAW12 */
	{0x3443, 0x01},
	{0x3444, 0x20},
	{0x3445, 0x25},
	{0x3446, 0x57},
	{0x3447, 0x00},
	{0x3448, 0x37},
	{0x3449, 0x00},
	{0x344A, 0x1F},
	{0x344B, 0x00},
	{0x344C, 0x1F},
	{0x344D, 0x00},
	{0x344E, 0x1F},
	{0x344F, 0x00},
	{0x3450, 0x77},
	{0x3451, 0x00},
	{0x3452, 0x1F},
	{0x3453, 0x00},
	{0x3454, 0x17},
	{0x3455, 0x00},
	{0x3472, 0x9c},
	{0x3473, 0x07},/* x-out size */
	{0x3480, 0x49},
	/* stanby cancel */
	/*
	 *{0x3000, 0x00},
	 *{0x3001, 0x00},
	 *{0x3002, 0x00},
	 */
	/* 0x308c, 0x11, */
	/* gain: 0x3014 0x3009 */
	/* shutter: 0x3020 0x3021 0x3022*/

	/* gain: 0x34a8, 0x34a9 */
	/* shutter: 0x34b1 0x34b2 0x34b3 */

};

/*
 * 4lane init
 */
/* raw12 */
/* .mfreq = 1485*100*1000,	mipi-csi clock */
static const cc_reg_t imx307_mipi_720p[] = {
	/* sensor stanby */
	{0x3000, 0x01},
	{0x3001, 0x01},
	{0x3002, 0x01},

	/* setting */
	{0x3005, 0x01},
	{0x3007, 0x10},
	{0x3009, 0x02},
	{0x300a, 0xf0},
	{0x3011, 0x0a},
	{0x3018, 0xee},
	{0x3019, 0x02},
	{0x301c, 0xc8},
	{0x301d, 0x19},
	{0x3046, 0x01},
	{0x304b, 0x0a},
	{0x305c, 0x20},
	{0x305d, 0x00},
	{0x305e, 0x20},
	{0x305f, 0x01},
	{0x309e, 0x4a},
	{0x309f, 0x4a},

	/* color bar */

	{0x311c, 0x0e},
	{0x3128, 0x04},
	{0x3129, 0x00},
	{0x313b, 0x41},
	{0x315e, 0x1a},
	{0x3164, 0x1a},
	{0x317c, 0x00},
	{0x31ec, 0x0e},

	{0x3405, 0x20},
	{0x3407, 0x03},
	{0x3414, 0x04},
	{0x3418, 0xd9},
	{0x3419, 0x02},
	/* 0A0Ah: RAW10, 0C0Ch: RAW12 */
	{0x3441, 0x0c},
	{0x3442, 0x0c},
	{0x3443, 0x03},
	{0x3444, 0x20},
	{0x3445, 0x25},
	{0x3446, 0x47},
	{0x3447, 0x00},
	{0x3448, 0x17},
	{0x3449, 0x00},
	{0x344a, 0x0f},
	{0x344b, 0x00},
	{0x344c, 0x0f},
	{0x344d, 0x00},
	{0x344e, 0x0f},
	{0x344f, 0x00},
	{0x3450, 0x2b},
	{0x3451, 0x00},
	{0x3452, 0x0b},
	{0x3453, 0x00},
	{0x3454, 0x0f},
	{0x3455, 0x00},
	{0x3472, 0x1c},
	{0x3473, 0x05},
	{0x3480, 0x49},

	/* stanby cancel */
	/* 0x3000, 0x00, */
	/* 0x3001, 0x00, */
	/* 0x3002, 0x00, */
};
/*
 * 4lane init
 */
/* raw12 */
/* .mfreq = 1485*100*1000,	mipi-csi clock */
static const cc_reg_t imx307_mipi_720p_2fdol_15fps[] = {
	/* sensor stanby */
	{0x3000, 0x01},
	{0x3001, 0x01},
	{0x3002, 0x01},

	/* setting */
	{0x3005, 0x01},
	{0x3405, 0x20},
	{0x3106, 0x11},
	{0x3007, 0x10},
	{0x3407, 0x03},
	{0x3009, 0x02},
	{0x300a, 0xf0},
	{0x300c, 0x11},
	{0x3011, 0x0a},
	{0x3414, 0x04},
	{0x3415, 0x00},
	{0x3018, 0xee},
	{0x3418, 0xc6},
	{0x3019, 0x02},
	{0x3419, 0x05},
	{0x301c, 0xc8},
	{0x311c, 0x0e},
	{0x301d, 0x19},
	{0x3020, 0x02},
	{0x3021, 0x00},
	{0x3024, 0x7B},
	{0x3025, 0x05},
	{0x3128, 0x04},
	{0x3129, 0x00},
	{0x3030, 0x09},
	{0x3031, 0x00},
	{0x313b, 0x41},
	{0x3441, 0x0c},
	{0x3442, 0x0c},
	{0x3443, 0x03},
	{0x3444, 0x20},
	{0x3045, 0x05},
	{0x3445, 0x25},
	{0x3046, 0x01},
	{0x3446, 0x47},
	{0x3447, 0x00},
	{0x3448, 0x17},
	{0x3449, 0x00},
	{0x344a, 0x0f},
	{0x304b, 0x0a},
	{0x344b, 0x00},
	{0x344c, 0x0f},
	{0x344d, 0x00},
	{0x344e, 0x0f},
	{0x344f, 0x00},
	{0x3450, 0x2b},
	{0x3451, 0x00},
	{0x3452, 0x0b},
	{0x3453, 0x00},
	{0x3454, 0x0f},
	{0x3455, 0x00},
	{0x305c, 0x20},
	{0x305d, 0x00},
	{0x305e, 0x20},
	{0x315e, 0x1a},
	{0x305f, 0x01},
	{0x3164, 0x1a},
	{0x3472, 0x20},
	{0x3473, 0x05},
	{0x347b, 0x23},
	{0x317c, 0x00},
	{0x3480, 0x49},
	{0x309e, 0x4a},
	{0x309f, 0x4a},
	{0x31ec, 0x0e},

	/* stanby cancel */
	/* 0x3000, 0x00, */
	/* 0x3001, 0x00, */
	/* 0x3002, 0x00, */
};
/*
 * 4lane init
 */
/* raw10 */
/* .mfreq = 1485*100*1000,	mipi-csi clock */
static const cc_reg_t imx307_mipi_720p_3fdol_7p5fps[] = {
	/* sensor stanby */
	{0x3000, 0x01},
	{0x3001, 0x01},
	{0x3002, 0x01},

	/* setting */
	{0x3005, 0x01},
	{0x3405, 0x20},
	{0x3106, 0x33},
	{0x3007, 0x10},
	{0x3407, 0x03},
	{0x3009, 0x02},
	{0x300a, 0xf0},
	{0x300c, 0x21},
	{0x3011, 0x0a},
	{0x3414, 0x04},
	{0x3415, 0x00},
	{0x3018, 0xee},
	{0x3418, 0x51},
	{0x3019, 0x02},
	{0x3419, 0x09},
	{0x301c, 0xc8},
	{0x311c, 0x0e},
	{0x301d, 0x19},
	{0x3020, 0x05},
	{0x3021, 0x00},
	{0x3024, 0xAB},
	{0x3025, 0x00},
	{0x3028, 0xB7},
	{0x3029, 0x01},
	{0x3128, 0x04},
	{0x3129, 0x00},
	{0x3030, 0xA6},
	{0x3031, 0x00},
	{0x3034, 0xB6},
	{0x3035, 0x00},
	{0x313b, 0x41},
	{0x3441, 0x0c},
	{0x3442, 0x0c},
	{0x3443, 0x03},
	{0x3444, 0x20},
	{0x3045, 0x05},
	{0x3445, 0x25},
	{0x3046, 0x01},
	{0x3446, 0x47},
	{0x3447, 0x00},
	{0x3448, 0x17},
	{0x3449, 0x00},
	{0x344a, 0x0f},
	{0x304b, 0x0a},
	{0x344b, 0x00},
	{0x344c, 0x0f},
	{0x344d, 0x00},
	{0x344e, 0x0f},
	{0x344f, 0x00},
	{0x3450, 0x2b},
	{0x3451, 0x00},
	{0x3452, 0x0b},
	{0x3453, 0x00},
	{0x3454, 0x0f},
	{0x3455, 0x00},
	{0x305c, 0x20},
	{0x305d, 0x00},
	{0x305e, 0x20},
	{0x315e, 0x1a},
	{0x305f, 0x01},
	{0x3164, 0x1a},
	{0x3472, 0x20},
	{0x3473, 0x05},
	{0x347b, 0x23},
	{0x317c, 0x00},
	{0x3480, 0x49},
	{0x309e, 0x4a},
	{0x309f, 0x4a},
	{0x31ec, 0x0e},
	/* RAW10 output */
	{0x3005, 0x00},
	{0x300a, 0x3c},
	{0x3418, 0xfa},
	{0x3419, 0x08},
	{0x3020, 0x04},
	{0x3024, 0x59},
	{0x3129, 0x1d},
	{0x3030, 0x55},
	{0x3034, 0x5f},
	{0x3441, 0x0a},
	{0x3442, 0x0a},
	{0x3046, 0x00},
	{0x317c, 0x12},
	{0x31ec, 0x37},

	/* stanby cancel */
	/* 0x3000, 0x00, */
	/* 0x3001, 0x00, */
	/* 0x3002, 0x00, */
};

/*
 * 2lane init
 */
/* raw12 */
/* .mfreq = 300*1000*1000,	mipi-csi clock */
static const cc_reg_t imx307_mipi_720p_2lane[] = {
	{0x3000, 0x01},
	{0x3001, 0x01},
	{0x3002, 0x01},
	{0x3005, 0x01},
	{0x3007, 0x10},
	{0x3009, 0x02},
	{0x300a, 0xf0},
	{0x3011, 0x0a},
	{0x3018, 0xee},
	{0x3019, 0x02},
	{0x301c, 0xc8},
	{0x301d, 0x19},
	{0x3046, 0x01},
	{0x304b, 0x0a},
	{0x305c, 0x20},
	{0x305d, 0x00},
	{0x305e, 0x20},
	{0x305f, 0x01},
	{0x309e, 0x4a},
	{0x309f, 0x4a},

	{0x311c, 0x0e},
	{0x3128, 0x04},
	{0x3129, 0x00},
	{0x313b, 0x41},
	{0x315e, 0x1a},
	{0x3164, 0x1a},
	{0x317c, 0x00},
	{0x31ec, 0x0e},
	{0x3405, 0x10},
	{0x3407, 0x01},
	{0x3414, 0x04},
	{0x3418, 0xd9},
	{0x3419, 0x02},
	{0x3441, 0x0c},
	{0x3442, 0x0c},
	{0x3443, 0x01},
	{0x3444, 0x20},
	{0x3445, 0x25},
	{0x3446, 0x4F},
	{0x3447, 0x00},
	{0x3448, 0x2F},
	{0x3449, 0x00},
	{0x344a, 0x17},
	{0x344b, 0x00},
	{0x344c, 0x17},
	{0x344d, 0x00},
	{0x344e, 0x17},
	{0x344f, 0x00},
	{0x3450, 0x57},
	{0x3451, 0x00},
	{0x3452, 0x17},
	{0x3453, 0x00},
	{0x3454, 0x17},
	{0x3455, 0x00},
	{0x3472, 0x1c},
	{0x3473, 0x05},
	{0x3480, 0x49},

	/* stanby cancel */
	/* 0x3000, 0x00, */
	/* 0x3001, 0x00, */
	/* 0x3002, 0x00, */
};

static struct cc_power_act power_up_act[] = {
	{CC_RESET, CC_DOWN},
	{CC_DELAY, 10},
	{CC_PWDN, CC_DOWN},
	{CC_DELAY, 10},
	{CC_PWDN, CC_UP},
	{CC_DELAY, 20},
	{CC_RESET, CC_UP},
	{CC_DELAY, 20},
};

static struct cc_power_act power_down_act[] = {
	{CC_RESET, CC_DOWN},
	{CC_DELAY, 10},
};


#define FHD_WIDTH_720		1280
#define FHD_HEIGHT_720		720
#define FHD_WIDTH_1080		1920
#define FHD_HEIGHT_1080		1080

static const struct cc_frame_cfg frame_cfgs[] = {
	{
		.width = FHD_WIDTH_720,
		.height = FHD_HEIGHT_720,
		.framerate = { 1, 30 }, /* 30fps */
		.reg_cfgs[0].regs = imx307_mipi_720p,
		.reg_cfgs[0].regs_cnt = ARRAY_SIZE(imx307_mipi_720p),
		/* .framerate = { 1, 15 },15fps */
		.mfreq = 1485*100*1000,	/* mipi-csi clock */
		/* .mfreq = 300*1000*1000, only for 2 lanes mipi-csi clock */
		.left = 0,
		.top = 0,
		.pclk = 149*1000*1000,
		.vts = 1125,
		.hts = 4400,
		.dlanes = 4,
		.mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
		.colorspace = V4L2_COLORSPACE_JPEG,
		.field = V4L2_FIELD_NONE,
	},
	{
		.width = FHD_WIDTH_1080,
		.height = FHD_HEIGHT_1080,
		.framerate = { 1, 30 }, /* 30fps */
		.reg_cfgs[0].regs = init_reg_list,
		.reg_cfgs[0].regs_cnt = ARRAY_SIZE(init_reg_list),
		/* .framerate = { 1, 15 },15fps */
		.mfreq = 445*1000*1000,	/* mipi-csi clock */
		.left = 0,
		.top = 0,
		.pclk = 149*1000*1000,
		.vts = 1125,
		.hts = 4400,
		.dlanes = 2,
		.mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
		.colorspace = V4L2_COLORSPACE_JPEG,
		.field = V4L2_FIELD_NONE,
	},
	{
		.width = FHD_WIDTH_720,
		.height = FHD_HEIGHT_720 * 2,
		.sub_width = FHD_WIDTH_720,
		.sub_height = FHD_HEIGHT_720,
		.sort = VISS_VERTICAL,
		.ch_number = 2,
		.wdr_mode = CC_WDR_DOL,
		.framerate = { 1, 15 }, /* 30fps */
		.reg_cfgs[0].regs = imx307_mipi_720p_2fdol_15fps,
		.reg_cfgs[0].regs_cnt =
				ARRAY_SIZE(imx307_mipi_720p_2fdol_15fps),
		/* .framerate = { 1, 15 },15fps */
		.mfreq = 1485*100*1000, /* mipi-csi clock */
		/* .mfreq = 300*1000*1000, only for 2 lanes mipi-csi clock */
		.left = 0,
		.top = 0,
		.pclk = 149*1000*1000,
		.vts = 1125,
		.hts = 4400,
		.dlanes = 4,
		.mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
		.colorspace = V4L2_COLORSPACE_JPEG,
		.field = V4L2_FIELD_NONE,
	},

	{
		.width = FHD_WIDTH_720,
		.height = FHD_HEIGHT_720 * 3,
		.sub_width = FHD_WIDTH_720,
		.sub_height = FHD_HEIGHT_720,
		.sort = VISS_HORIZONTAL,
		.ch_number = 3,
		.wdr_mode = CC_WDR_DOL,
		.framerate = { 10, 75 }, /* 30fps */
		.reg_cfgs[0].regs = imx307_mipi_720p_3fdol_7p5fps,
		.reg_cfgs[0].regs_cnt =
				ARRAY_SIZE(imx307_mipi_720p_3fdol_7p5fps),
		/* .framerate = { 1, 15 },15fps */
		.mfreq = 1485*100*1000,	/* mipi-csi clock */
		/* .mfreq = 300*1000*1000, only for 2 lanes mipi-csi clock */
		.left = 0,
		.top = 0,
		.pclk = 149*1000*1000,
		.vts = 1125,
		.hts = 4400,
		.dlanes = 4,
		.mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
		.colorspace = V4L2_COLORSPACE_JPEG,
		.field = V4L2_FIELD_NONE,
	},
};

static int sensor_detect(struct cc_sensor_info *cc_si)
{

	u8 tmp;
	int ret = 0;
	u16 id;

	ret = cc_si->read_reg(cc_si, 0x31DC, &tmp);
	if (ret != 0) {
		PRT_ERR("tmp =%x\n", tmp);
		return -ENODEV;
	}

	id = tmp & 0x06;
	if (id != 0x4) {
		PRT_ERR("ID wrong! (0x%x)\n", id);
		return -ENODEV;
	}


	return 0;
}

static int sensor_init(struct cc_sensor_info *cc_si)
{
	struct timespec ts_s;
	struct timespec ts_f;
	int ret = 0;

	ret = sensor_detect(cc_si);
	if (ret != 0)
		return ret;

	ktime_get_ts(&ts_s);

	ktime_get_ts(&ts_f);

	PRT_INFO("timeused %d %d\n", (int)(ts_f.tv_sec - ts_s.tv_sec),
		(int)(ts_f.tv_nsec - ts_s.tv_nsec));

	return 0;
}

static int sensor_set_stream(struct cc_sensor_info *cc_si, int on)
{
	int ret = 0;

	PRT_INFO("*************************on:%d\n", on);

	if (cc_si->vflip && cc_si->mirror) {
		ret = cc_si->write_reg(cc_si, 0x3007, 0x03);
		if (ret != 0) {
			PRT_ERR("set mirror failed ret: %d.\n", ret);
			return ret;
		}
	} else if (cc_si->vflip) {
		ret = cc_si->write_reg(cc_si, 0x3007, 0x01);
		if (ret != 0) {
			PRT_ERR("set mirror failed ret: %d.\n", ret);
			return ret;
		}
	} else if (cc_si->mirror) {
		ret = cc_si->write_reg(cc_si, 0x3007, 0x02);
		if (ret != 0) {
			PRT_ERR("set mirror failed ret: %d.\n", ret);
			return ret;
		}
	}

	if (on) {
		ret = cc_si->write_reg(cc_si, 0x3000, 0x00);
		ret += cc_si->write_reg(cc_si, 0x3001, 0x00);
		ret += cc_si->write_reg(cc_si, 0x3002, 0x00);
		if (ret)
			PRT_ERR("%d\n", on);
	} else {
		ret = cc_si->write_reg(cc_si, 0x3000, 0x01);
		ret += cc_si->write_reg(cc_si, 0x3001, 0x01);
		ret += cc_si->write_reg(cc_si, 0x3002, 0x01);
		if (ret)
			PRT_ERR("%d\n", on);
	}

	return ret;
}

/*
 * ctrl for sensor start
 */

static int get_reg(struct cc_sensor_info *cc_si,
			struct viss_isp_reg_data *reg_data)
{
	u8 val = 0;
	int ret = 0;

	/* read */
	ret = cc_si->read_reg(cc_si, reg_data->reg_add, &val);
	reg_data->reg_value = val;
	return ret;
}

static int set_reg(struct cc_sensor_info *cc_si,
			struct viss_isp_reg_data *reg_data)
{
	u8 val = reg_data->reg_value;
	u8 read_val;
	int ret;

	/* write */
	ret = cc_si->write_reg(cc_si, reg_data->reg_add, val);
	PRT_DBG("addr=0x%x,val=0x%02x.\n",
		reg_data->reg_add, reg_data->reg_value);

	ret = cc_si->read_reg(cc_si, reg_data->reg_add, &read_val);
	if (read_val != val)
		PRT_DBG("ret=%d,reg_add=0x%x,val=0x%02x,read_val=0x%02x\n",
			ret, reg_data->reg_add, val, read_val);

	return ret;
}

static int viss_get_sensor_fmt_info(struct cc_sensor_info *cc_si,
		struct viss_isp_sensor_fmt_info *sensor_fmt_info)
{
	int ret = 0;
	sensor_fmt_info->pclk = cc_si->cur_frmcfg->pclk;
	sensor_fmt_info->vts = cc_si->cur_frmcfg->vts;
	sensor_fmt_info->hts = cc_si->cur_frmcfg->hts;
	sensor_fmt_info->hoffset = cc_si->cur_frmcfg->left;
	sensor_fmt_info->voffset = cc_si->cur_frmcfg->top;
	sensor_fmt_info->width = cc_si->cur_frmcfg->width;
	sensor_fmt_info->height = cc_si->cur_frmcfg->height;
	sensor_fmt_info->bayer_mode = ISP_PATTERN_RGGB;
	sensor_fmt_info->buswidth = ISP_INPUT_12BITS;

	return ret;
}

static int viss_isp_set_shutter(struct cc_sensor_info *cc_si,
				__u32 shutter_time)
{
	int ret = 0;
	u32 vmax, shs;

	if (shutter_time < 2)
		shutter_time = 2;

	if (shutter_time > VMAX - 2)
		vmax = shutter_time + 2;
	else
		vmax = VMAX;
	ret += cc_si->write_reg(cc_si, 0x3018, ((vmax >> 0) & 0xff));
	ret += cc_si->write_reg(cc_si, 0x3019, ((vmax >> 8) & 0xff));

	shs = vmax - (shutter_time + 1);

	ret += cc_si->write_reg(cc_si, 0x3020, ((shs >> 0) & 0xff));
	ret += cc_si->write_reg(cc_si, 0x3021, ((shs >> 8) & 0xff));
	ret += cc_si->write_reg(cc_si, 0x3022, ((shs >> 16) & 0x3));

	return ret;
}

static u32 gain_table[231] = {
	   256,    264,    274,    283,    293,    304,    314,    326,
	   337,    349,    361,    374,    387,    401,    415,    429,
	   444,    460,    476,    493,    510,    528,    547,    566,
	   586,    607,    628,    650,    673,    697,    721,    746,
	   773,    800,    828,    857,    887,    918,    951,    984,
	  1019,   1054,   1092,   1130,   1170,   1211,   1253,   1297,
	  1343,   1390,   1439,   1490,   1542,   1596,   1652,   1710,
	  1771,   1833,   1897,   1964,   2033,   2104,   2178,   2255,
	  2334,   2416,   2501,   2589,   2680,   2774,   2872,   2973,
	  3077,   3185,   3297,   3413,   3533,   3657,   3786,   3919,
	  4057,   4199,   4347,   4500,   4658,   4822,   4991,   5167,
	  5348,   5536,   5731,   5932,   6141,   6356,   6580,   6811,
	  7050,   7298,   7555,   7820,   8095,   8379,   8674,   8979,
	  9294,   9621,   9959,  10309,  10671,  11046,  11435,  11836,
	 12252,  12683,  13129,  13590,  14068,  14562,  15074,  15604,
	 16152,  16720,  17307,  17915,  18545,  19197,  19871,  20570,
	 21293,  22041,  22816,  23617,  24447,  25306,  26196,  27116,
	 28069,  29056,  30077,  31134,  32228,  33361,  34533,  35747,
	 37003,  38303,  39649,  41043,  42485,  43978,  45523,  47123,
	 48779,  50494,  52268,  54105,  56006,  57974,  60012,  62121,
	 64304,  66564,  68903,  71324,  73831,  76425,  79111,  81891,
	 84769,  87748,  90832,  94024,  97328, 100748, 104289, 107954,
	111748, 115675, 119740, 123948, 128303, 132812, 137480, 142311,
	147312, 152489, 157848, 163395, 169137, 175081, 181234, 187603,
	194195, 201020, 208084, 215397, 222966, 230802, 238913, 247309,
	256000, 264996, 274308, 283948, 293927, 304256, 314948, 326016,
	337473, 349333, 361609, 374317, 387471, 401088, 415183, 429773,
	444877, 460510, 476694, 493446, 510787, 528737, 547318, 566552,
	586462, 607071, 628405, 650489, 673348, 697011, 721506
};

static char gain_mode_buf = 0x02;
static int viss_isp_set_gain(struct cc_sensor_info *cc_si, int gain_val)
{
	int ret;
	u8 rdval, db_idx;
	char gain_mode = 0x02;

	ret = cc_si->read_reg(cc_si, 0x3009, &rdval);
	if (ret != 0)
		return ret;

	if (gain_val < 1 * 256)
		gain_val = 256;

	for (db_idx = 0; db_idx < 231; db_idx++) {
		if (gain_val <= gain_table[db_idx])
			break;
	}

	/* 21*0.3db = 6.3db > 2x gain */
	if (db_idx >= 21) {
		gain_mode = rdval | 0x10;
		ret = cc_si->write_reg(cc_si, 0x3014, db_idx - 20);
	} else {
		gain_mode = rdval & 0xef;
		ret = cc_si->write_reg(cc_si, 0x3014, db_idx);
	}

	/*
	 * usleep_range(14000, 15000);
	 * ret = cc_si->write_reg(cc_si, 0x3009, gain_mode);
	 */
	gain_mode_buf = gain_mode;
	schedule_delayed_work(&cc_si->sensor_work, msecs_to_jiffies(45));

	return 0;
}

static int viss_isp_exp_ctrl(struct cc_sensor_info *cc_si,
				struct viss_isp_exp_gain *exp_gain)
{
	int ret = 0;
	/* return 0; */
	ret = cc_si->write_reg(cc_si, 0x3001, 0x01);

	ret = viss_isp_set_shutter(cc_si, exp_gain->exp_time / 16);
	if (ret < 0) {
		PRT_ERR("viss_isp_set_shutter failed %d.\n", ret);
		PRT_ERR("%u, %u.\n",
			exp_gain->exp_time, exp_gain->exp_gain);
		return -ENODEV;
	}

	ret = viss_isp_set_gain(cc_si, exp_gain->exp_gain);
	if (ret < 0) {
		PRT_ERR("viss_isp_set_gain failed %d.\n", ret);
		PRT_ERR("%u, %u.\n",
			exp_gain->exp_time, exp_gain->exp_gain);
		return -ENODEV;
	}
	ret = cc_si->write_reg(cc_si, 0x3001, 0x00);

	return ret;
}

static void sensor_gain_switch(struct work_struct *work)
{
	struct cc_sensor_info *cc_si = container_of(to_delayed_work(work),
				struct cc_sensor_info, sensor_work);
	/* HCG and LCG mode switch */
	cc_si->write_reg(cc_si, 0x3009, gain_mode_buf);
}

static long sensor_ioctl(struct cc_sensor_info *cc_si, unsigned int cmd,
				void *arg)
{
	int ret = 0;

	switch (cmd) {
	case VIDIOC_VISS_GET_SENSOR:
		ret = get_reg(cc_si, (struct viss_isp_reg_data *)arg);
		break;
	case VIDIOC_VISS_SET_SENSOR:
		ret = set_reg(cc_si, (struct viss_isp_reg_data *)arg);
		break;
	case VIDIOC_VISS_ISP_EXP_CTRL:
		ret = viss_isp_exp_ctrl(cc_si,
			(struct viss_isp_exp_gain *)arg);
		break;
	case VIDIOC_VISS_ISP_SENSOR_FMT_INFO:
		ret = viss_get_sensor_fmt_info(cc_si,
			(struct viss_isp_sensor_fmt_info *)arg);
		break;
	default:
		return -EINVAL;
	}
	return ret;
}

/*
 * ctrl for sensor finish
 */


#define DRIVER_NAME "imx307_mipi"

static int sensor_probe(struct platform_device *pdev)
{
	struct cc_sensor_info *cc_si = NULL;
	int ret = 0;

	PRT_INFO("*********probe begin\n");

	cc_si = devm_kzalloc(&pdev->dev, sizeof(*cc_si), GFP_KERNEL);
	if (cc_si == NULL) {
		PRT_ERR("Can't alloc memory!\n");
		return -ENOMEM;
	}

	cc_si->reg_bits = 16;
	cc_si->val_bits = 8;

	/* cc_si->dlanes = 2; */

	cc_si->name = DRIVER_NAME;

	cc_si->power_up_act = power_up_act;
	cc_si->power_down_act = power_down_act;
	cc_si->npwr_up_acts = ARRAY_SIZE(power_up_act);
	cc_si->npwr_dn_acts = ARRAY_SIZE(power_down_act);

	cc_si->framecfgs = frame_cfgs;
	cc_si->nfrmcfgs = ARRAY_SIZE(frame_cfgs);

	ret = cc_sensor_probe(pdev, cc_si);
	if (ret < 0)
		return ret;

	cc_si->detect = sensor_detect;
	cc_si->sensor_init = sensor_init;
	cc_si->set_stream = sensor_set_stream;
	cc_si->sensor_ioctl = sensor_ioctl;

	INIT_DELAYED_WORK(&cc_si->sensor_work, sensor_gain_switch);

	PRT_INFO("probe end\n");
	return 0;
}

static int sensor_remove(struct platform_device *pdev)
{
	struct cc_sensor_info *cc_si = platform_get_drvdata(pdev);

	cc_sensor_remove(pdev);

	devm_kfree(&pdev->dev, cc_si);

	return 0;
}

static const struct of_device_id imx307_mipi_of_match[] = {
	{
		.compatible = DRIVER_NAME,
	},
	{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, imx307_mipi_of_match);

static struct platform_driver imx307_mipi_driver = {
	.driver = {
		.of_match_table	= imx307_mipi_of_match,
		.name		= DRIVER_NAME,
		.owner		= THIS_MODULE,
	},
	.probe = sensor_probe,
	.remove = sensor_remove,
};

module_platform_driver(imx307_mipi_driver);

MODULE_DESCRIPTION("IMX307-MIPI sensor driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRIVER_NAME);

